#![allow(dead_code, unused)]

use std::{
    collections::HashMap, ffi::*, ptr::{copy, copy_nonoverlapping, null, null_mut}, sync::{Arc, Mutex}
};
use ash::vk::{self, Handle};
use getset::{CloneGetters, CopyGetters, Getters, MutGetters, Setters, WithSetters};
use crate::engine::njm_device::NjmDevice;
use std::io::Cursor;
use image::{GenericImageView, ImageReader};

#[derive(Debug, Clone, Copy)]
pub enum NjmImgFormat {
    NjmImgFormatUnknown,
    NjmImgFormatRgb8,
    NjmImgFormatRgba8,
    NjmImgFormatRgb16f,
    NjmImgFormatRgba16f,
    NjmImgFormatRgb32f,
    NjmImgFormatRgba32f,
}

impl Default for NjmImgFormat {
    fn default() -> Self {
        return Self::NjmImgFormatUnknown;
    }
}

pub enum NjmImgExt {
    NjmImgExtUnknown,
    NjmImgExtPng,
    NjmImgExtJpg,
    NjmImgExtBmp,
    NjmImgExtTga,
}

impl Default for NjmImgExt {
    fn default() -> Self {
        return Self::NjmImgExtUnknown;
    }
}

pub type id_t = String;
pub type Map = HashMap<id_t, Arc<NjmImg>>;

#[derive(Default, Getters, Setters, WithSetters, MutGetters, CopyGetters, CloneGetters)]
pub struct NjmImg {
    #[getset(get = "pub", set = "pub")]
    name: String,

    #[getset(get = "pub", set = "pub")]
    format: NjmImgFormat,

    #[getset(get = "pub", set = "pub")]
    width: u32,

    #[getset(get = "pub", set = "pub")]
    height: u32,

    #[getset(get = "pub", set = "pub")]
    data: Vec<u8>,
}

impl NjmImg {
    // associated helper
    pub fn get_vk_fmt_from_njm_fmt(njm_fmt: NjmImgFormat) -> vk::Format {
        match njm_fmt {
            NjmImgFormat::NjmImgFormatRgb8      =>  vk::Format::R8G8B8_UNORM,
            NjmImgFormat::NjmImgFormatRgba8     =>  vk::Format::R8G8B8A8_SNORM,
            NjmImgFormat::NjmImgFormatRgb16f    =>  vk::Format::R16G16B16_SFLOAT,
            NjmImgFormat::NjmImgFormatRgba16f   =>  vk::Format::R16G16B16A16_SFLOAT,
            NjmImgFormat::NjmImgFormatRgb32f    =>  vk::Format::R32G32B32_SFLOAT,
            NjmImgFormat::NjmImgFormatRgba32f   =>  vk::Format::R32G32B32A32_SFLOAT,
            NjmImgFormat::NjmImgFormatUnknown   =>  vk::Format::UNDEFINED,
        }
    }
    pub fn get_njm_fmt_from_vk_fmt(vk_fmt: vk::Format) -> NjmImgFormat {
        match vk_fmt {
            vk::Format::R8G8B8_UNORM        =>  NjmImgFormat::NjmImgFormatRgb8,
            vk::Format::R8G8B8A8_SNORM      =>  NjmImgFormat::NjmImgFormatRgba8,
            vk::Format::R16G16B16_SFLOAT    =>  NjmImgFormat::NjmImgFormatRgb16f,
            vk::Format::R16G16B16A16_SFLOAT =>  NjmImgFormat::NjmImgFormatRgba16f,
            vk::Format::R32G32B32_SFLOAT    =>  NjmImgFormat::NjmImgFormatRgb32f,
            vk::Format::R32G32B32A32_SFLOAT =>  NjmImgFormat::NjmImgFormatRgba32f,
            vk::Format::UNDEFINED           =>  NjmImgFormat::NjmImgFormatUnknown,
            _                               =>  NjmImgFormat::NjmImgFormatUnknown, 
        }
    }

    pub fn get_inner_fmt_from_njm_fmt(njm_fmt: NjmImgFormat) -> image::ColorType {
        match njm_fmt {
            NjmImgFormat::NjmImgFormatRgb8      =>  image::ColorType::Rgb8,
            NjmImgFormat::NjmImgFormatRgba8     =>  image::ColorType::Rgba8,
            NjmImgFormat::NjmImgFormatRgb16f    =>  image::ColorType::Rgb16,
            NjmImgFormat::NjmImgFormatRgba16f   =>  image::ColorType::Rgba16,
            NjmImgFormat::NjmImgFormatRgb32f    =>  image::ColorType::Rgb32F,
            NjmImgFormat::NjmImgFormatRgba32f   =>  image::ColorType::Rgba32F,
            NjmImgFormat::NjmImgFormatUnknown   =>  panic!("njm format is unknow"),
        }
    }
    pub fn get_njm_fmt_from_inner_fmt(inner_fmt: image::ColorType) -> NjmImgFormat {
        match inner_fmt {
            image::ColorType::Rgb8      =>  NjmImgFormat::NjmImgFormatRgb8,
            image::ColorType::Rgba8     =>  NjmImgFormat::NjmImgFormatRgba8,
            image::ColorType::Rgb16     =>  NjmImgFormat::NjmImgFormatRgb16f,
            image::ColorType::Rgba16    =>  NjmImgFormat::NjmImgFormatRgba16f,
            image::ColorType::Rgb32F    =>  NjmImgFormat::NjmImgFormatRgb32f,
            image::ColorType::Rgba32F   =>  NjmImgFormat::NjmImgFormatRgba32f,
            _                           =>  NjmImgFormat::NjmImgFormatUnknown, 
        }
    }

    // public api
    pub fn new(name: &str, img_path: &str) -> Self {
        let img = image::open(img_path).unwrap();
        let fmt = img.color();
        let njm_fmt = Self::get_njm_fmt_from_inner_fmt(fmt);
        let (w, h) = img.dimensions();
        let data = img.into_bytes();

        return Self {
            name: name.to_string(),
            format: njm_fmt,
            width: w,
            height: h,
            data: data,
        };
    }

    pub fn save(&self, file_path: &str) {
        let ext_color_fmt: image::ExtendedColorType = Self::get_inner_fmt_from_njm_fmt(self.format).into();
        image::save_buffer(file_path, &self.data, self.width, self.height, ext_color_fmt);
    }

    pub fn get_vk_extent(&self) -> vk::Extent2D {
        return vk::Extent2D {
            width: self.width,
            height: self.height,
        };
    }

    pub fn get_vk_format(&self) -> vk::Format {
        return Self::get_vk_fmt_from_njm_fmt(self.format);
    }

    pub fn test() -> Result<(), Box<dyn std::error::Error>> {
        // let im = image::open("p")?;
        // let img = ImageReader::open("myimage.png")?.decode()?;
        // let img2 = ImageReader::new(Cursor::new(bytes)).with_guessed_format()?.decode()?;
        // img.save("empty.jpg")?;
        // let mut bytes: Vec<u8> = Vec::new();
        // img2.write_to(&mut Cursor::new(&mut bytes), image::ImageFormat::Png)?;
        Ok(())
    }
}

